package com.qing.annotation.factory.module;

import com.qing.annotation.GenerateCode;
import com.qing.annotation.exception.IdAlreadyUsedException;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import javax.annotation.processing.Filer;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 同一工厂类的集合
 *
 * @author qing
 * 2023/9/17
 */
public class SimpleFactoryGroupedClasses implements GenerateCode {

    /**
     * 自动生成的文件后缀 xxx_AutoSimpleFactory
     */
    private static final String SUFFIX = "_AutoSimpleFactory";

    /**
     * 待实现工厂的全类名
     */
    private final String mQualifiedClassName;

    private final Map<String, SimpleFactoryAnnotatedClass> mItemsMap = new LinkedHashMap<>();

    public SimpleFactoryGroupedClasses(String qualifiedClassName) {
        mQualifiedClassName = qualifiedClassName;
    }

    /**
     * 添加工厂对象到集合中
     *
     * @param toInsert 带添加元素
     * @throws IdAlreadyUsedException 若id已存在，则会抛出此异常
     */
    public void add(SimpleFactoryAnnotatedClass toInsert) throws IdAlreadyUsedException {
        SimpleFactoryAnnotatedClass existing = mItemsMap.get(toInsert.getId());
        if (existing != null) {
            throw new IdAlreadyUsedException(existing);
        }
        mItemsMap.put(toInsert.getId(), toInsert);
    }

    /**
     * 生产代码
     *
     * @param elementUtils
     * @param filer
     * @throws IOException
     */
    @Override
    public void generateCode(Elements elementUtils, Filer filer) throws IOException {

        TypeElement superClassName = elementUtils.getTypeElement(mQualifiedClassName);
        String factoryClassName = superClassName.getSimpleName() + SUFFIX;
        PackageElement pkg = elementUtils.getPackageOf(superClassName);
        String packageName = pkg.isUnnamed() ? "" : pkg.getQualifiedName().toString();

        MethodSpec.Builder createMethod = MethodSpec.methodBuilder("create").addModifiers(Modifier.PUBLIC).addParameter(String.class, "id").returns(TypeName.get(superClassName.asType()));

        // if id == null
        createMethod.beginControlFlow("if (id == null)").addStatement("throw new IllegalArgumentException(\"id is null!\")").endControlFlow();

        // 遍历注解实例
        for (SimpleFactoryAnnotatedClass item : mItemsMap.values()) {
            createMethod.beginControlFlow("if ($S.equals(id))", item.getId()).addStatement("return new $L()", item.getTypeElement().getQualifiedName().toString()).endControlFlow();
        }

        // 未定义id
        createMethod.addStatement("throw new IllegalArgumentException(\"Unknown id \" + id)");

        // 创建类
        TypeSpec typeSpec = TypeSpec.classBuilder(factoryClassName)
                .addModifiers(Modifier.PUBLIC)
                .addMethod(createMethod.build())
                .build();

        // 写入文件
        JavaFile.builder(packageName, typeSpec).build().writeTo(filer);
    }

}
